/**
 * 处理聊天结束事件，解析消息中的事件标签并执行相应操作，同时根据消息内容处理特殊展示。
 *
 * @param {string} assistantMessage - 助手发送的消息内容
 *
 * @param {HTMLElement} contentElement - 用于展示内容的 DOM 元素
 */
async function handleChatEndEvent(assistantMessage, contentElement) {
	/** 从助手消息中提取结论内容 */
	const extractedContent = extractConclusion(assistantMessage);
	// 若消息包含 markdown 代码块，则重新加载消息并处理 markdown
	if (/```markdown/i.test(assistantMessage)) {
		reloadMessageAndMarkdown(assistantMessage, contentElement);
	}
	// 否则生成集合渲染
	else generateCollectionRendering(contentElement);
	// 绑定代码执行按钮
	bindCodeExecuteButtons(contentElement);
	// 解析提取的内容中的事件标签并执行对应的处理函数
	await parseEventTag(extractedContent, 500);
	// 若长期记忆模式已启用，则永久化对话历史中的所有消息
	if (isLongTermMemoryMode) await persistChatMessages();
	// 若主动消息模式已启用，则在 5 分钟后触发主动延续对话的消息
	if (isActiveMessageMode) activeMessageAfterMinutes(5);
	// 若对话历史长度为 4，则启用长期记忆模式
	if (conversationHistory.length == 4 && !isLongTermMemoryMode) {
		// 更新长期记忆模式按钮图标为无限循环图标
		longTermMemoryButton.innerHTML = '<i class="fas fa-infinity"></i>';
		// 显示长期记忆模式已启用的系统消息
		showSystemMessage("启用< 长期记忆模式 >", "success");
		// 启用长期记忆模式
		isLongTermMemoryMode = true;
		// 切换长期记忆模式按钮样式
		longTermMemoryButton.classList.add("clicking");
	}
	// 若对话历史长度为 10，则启用主动消息模式
	if (conversationHistory.length == 10 && !isActiveMessageMode) {
		// 更新主动消息模式按钮图标为内存图标
		activeMessageButton.innerHTML = '<i class="fas fa-comment-dots"></i>';
		// 显示主动消息模式已启用的系统消息
		showSystemMessage("启用< 主动消息模式 >", "success");
		// 启用主动消息模式
		isActiveMessageMode = true;
		// 切换主动消息模式按钮样式
		activeMessageButton.classList.add("clicking");
	}
	// 取消主动发言
	DelayExecutionManager.cancel('主动延续话题');
};

/**
 * 解析输入内容中的事件标签，并根据标签调用对应的处理函数。
 *
 * 每个标签处理完成后，根据指定的延迟时间进行等待。
 *
 * @param {string} input - 待解析的输入内容，可能包含事件标签
 *
 * @param {number} [delayMs=10] - 每次处理标签后的延迟时间（毫秒），默认为 10 毫秒；若为 0 则不延迟
 */
async function parseEventTag(input, delayMs = 10) {
	/** 定义要处理的标签及其对应的回调函数 */
	const tagHandlers = [
		{
			tag: 'end_chat',
			handler: (intel) => {
				// 导出聊天交互数据
				exportChatInteractionWithFetch(intel);
				// 5 秒后清空会话历史和聊天历史面板
				setTimeout(() => { conversationHistory = []; chatHistoryPanel.innerHTML = ''; }, 5000);
			}
		}
	];
	// 遍历所有标签处理器
	for (const { tag, handler } of tagHandlers) {
		/** 构建正则表达式，用于匹配特定标签格式的内容 */
		const pattern = new RegExp(`<!-- ${tag}-(.*?) -->`, 'g');
		/** 用于存储正则匹配结果 */
		let match;
		// 循环匹配输入内容中的所有符合条件的标签
		while ((match = pattern.exec(input)) !== null) {
			/** 提取标签中的信息 */
			const intel = match[1];
			// 调用对应的处理函数处理提取的信息并等待处理完成
			await handler(intel);
			// 若延迟时间大于 0，则等待指定的延迟时间
			if (delayMs > 0) await new Promise(resolve => setTimeout(resolve, delayMs));
		}
	}
};

/**
 * 将对话历史中的所有消息永久化保存到知识库
 */
async function persistChatMessages() {
	// 遍历对话历史中的每条消息
	for (const message of conversationHistory) {
		/** 提取消息时间戳并替换冒号为下划线 */
		const timestamp = message.timestamp.replace(/:/g, '_');
		/** 构建文件名，格式为 "角色 时间戳" */
		const fileName = message.role + timestamp + '.knowledge';
		// 检查该文件名是否已在已完成下载历史记录中，若存在则跳过当前消息处理
		if (completeDownloadHistory.has(fileName)) continue;
		// 若消息内容为空或仅包含空格，则直接返回
		if (!message.content || message.content.trim().length === 0) continue;
		/** 发送 POST 请求: 保存文件内容 */
		const response = await fetch('/save',
			{
				method: 'POST',
				headers: {
					// 对文件名进行 Base64 编码后设置到请求头，指定文件保存路径
					'X-File-Name': toBtoaString('knowledge/entry/' + fileName),
					// 设置内容类型为 JSON
					'Content-Type': 'application/json',
					// 设置允许覆盖已存在的文件
					'X-Overwrite': 'true'
				},
				body: message.content.trim()
			}
		);
		/** 解析响应数据为 JSON 格式 */
		const data = await response.json();
		// 检查响应状态是否成功
		if (response.status !== 200) {
			showSystemMessage(data.message || '永久化聊天消息创建失败', 'error');
			continue;
		};
		// 将当前文件名添加到已完成下载历史记录中，避免重复处理
		completeDownloadHistory.add(fileName);
		// 可选：添加短暂延迟以避免请求过于频繁
		await new Promise(resolve => setTimeout(resolve, 100));
	}
	// 处理知识库文件
	processKnowledgeFiles();
};